package com.example.rentalclearing.service;

import cn.hutool.core.collection.CollUtil;
import com.example.rentalclearing.common.ThreadLocalClearingResult;
import com.example.rentalclearing.entity.BankCollectionRecord;
import com.example.rentalclearing.entity.RentalPaymentSchedule;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class BankRecordHandleService {

    private final IRentalPaymentScheduleService rentalPaymentScheduleService;
    private final IBankCollectionRecordService bankCollectionRecordService;
    private final PlatformTransactionManager transactionManager;

    /**
     * 处理单个租金计划（独立事务）
     */
    @Transactional
    public ThreadLocalClearingResult processSingleSchedule(RentalPaymentSchedule schedule) {
        // 1. 查询对应付款账户未用完的收款记录，按ID排序以避免死锁
        List<BankCollectionRecord> collections = bankCollectionRecordService.selectUnusedByPayerAccountName(schedule.getTenantName());
        if (CollUtil.isEmpty(collections)) {
            log.warn("付款账户未找到未用完的收款记录，付款账户名称：{}", schedule.getTenantName());
            return new ThreadLocalClearingResult();
        }

        // 2. 进行扣款处理
        return processScheduleWithCollections(schedule, collections);
    }


    /**
     * 使用银行收款记录核销单个租金计划
     * 为避免死锁，我们按收款记录ID排序处理
     */
    private ThreadLocalClearingResult processScheduleWithCollections(RentalPaymentSchedule schedule, List<BankCollectionRecord> collections) {
        ThreadLocalClearingResult result = new ThreadLocalClearingResult();

        // 按ID排序以避免死锁
        collections = collections.stream()
                .sorted((r1, r2) -> r1.getId().compareTo(r2.getId()))
                .collect(Collectors.toList());

        // 计算还需核销的金额（利息+本金）
        BigDecimal remainingInterest = schedule.getInterestUnreceived();
        BigDecimal remainingPrincipal = schedule.getPrincipalUnreceived();

        if (remainingInterest.compareTo(BigDecimal.ZERO) <= 0 && remainingPrincipal.compareTo(BigDecimal.ZERO) <= 0) {
            log.error("该租金计划已全部核销，ID：{}", schedule.getId());
            updateWriteOffStatus(schedule, result);
            return result;
        }

        // 遍历银行收款记录进行核销
        for (BankCollectionRecord record : collections) {
            // 为每个收款记录创建独立的事务，减少锁的持有时间
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            TransactionStatus status = transactionManager.getTransaction(def);
            
            try {
                // 重新查询收款记录以获取最新状态
                BankCollectionRecord currentRecord = bankCollectionRecordService.getById(record.getId());
                if (currentRecord == null) {
                    transactionManager.rollback(status);
                    continue;
                }
                
                BigDecimal availableAmount = currentRecord.getAvailableAmount();
                // 如果当前银行收款记录无可用金额，跳过
                if (availableAmount.compareTo(BigDecimal.ZERO) <= 0) {
                    updateUsageStatus(currentRecord);
                    transactionManager.commit(status);
                    continue;
                }

                // 已使用的金额
                BigDecimal usedAmount = BigDecimal.ZERO;
                // 先核销利息
                if (remainingInterest.compareTo(BigDecimal.ZERO) > 0) {
                    // 本次支付的利息 = 可用金额和需要核销的利息取较小者
                    BigDecimal interestToWrite = availableAmount.min(remainingInterest);
                    // 设置总共支付的利息
                    schedule.setInterestReceived(schedule.getInterestReceived().add(interestToWrite));
                    // 新的需要支付的利息=需要核销的利息-本次支付的利息
                    remainingInterest = remainingInterest.subtract(interestToWrite);
                    // 已使用金额 = 已使用金额+本次支付的利息
                    usedAmount = usedAmount.add(interestToWrite);
                    // 本次核销的利息统计到核销总利息中
                    result.addTotalInterest(interestToWrite);

                    // 新的可用金额 = 可用金额-已使用的金额
                    availableAmount = availableAmount.subtract(usedAmount);
                    // 如果当前银行收款记录无可用金额，则更新收款记录使用状态，并使用下一个收款记录来核销
                    if (availableAmount.compareTo(BigDecimal.ZERO) <= 0) {
                        currentRecord.setAmountUsed(currentRecord.getPaymentAmount());
                        updateUsageStatus(currentRecord);
                        transactionManager.commit(status);
                        continue;
                    }
                }

                // 再核销本金
                if (remainingPrincipal.compareTo(BigDecimal.ZERO) > 0) {
                    // 本次支付的本金 = 可用金额和需要核销的本金取较小者
                    BigDecimal principalToWrite = availableAmount.min(remainingPrincipal);
                    // 设置总共支付的本金
                    schedule.setPrincipalReceived(schedule.getPrincipalReceived().add(principalToWrite));
                    // 新的需要支付的本金=需要核销的本金-本次支付的本金
                    remainingPrincipal = remainingPrincipal.subtract(principalToWrite);
                    // 已使用金额 = 已使用金额+本次支付的本金
                    usedAmount = usedAmount.add(principalToWrite);
                    // 本次核销的本金统计到核销总本金中
                    result.addTotalPrincipal(principalToWrite);
                }

                // 更新银行收款记录已使用金额
                if (usedAmount.compareTo(BigDecimal.ZERO) > 0) {
                    currentRecord.setAmountUsed(currentRecord.getAmountUsed().add(usedAmount));
                    updateUsageStatus(currentRecord); // 更新银行收款使用状态
                }
                
                transactionManager.commit(status);

                // 判断租金计划的利息和本金的核销情况，如果都已经支付完则跳出循环
                if (remainingInterest.compareTo(BigDecimal.ZERO) <= 0 && remainingPrincipal.compareTo(BigDecimal.ZERO) <= 0) {
                    break;
                }
            } catch (Exception e) {
                transactionManager.rollback(status);
                log.error("处理收款记录时发生异常，记录ID：{}", record.getId(), e);
            }
        }

        // 更新租金核销状态
        schedule.setInterestReceived(schedule.getInterestReceivable().subtract(remainingInterest));
        schedule.setPrincipalReceived(schedule.getPrincipalReceivable().subtract(remainingPrincipal));
        updateWriteOffStatus(schedule, result);

        return result;
    }


    /**
     * 更新租金核销状态
     */
    private void updateWriteOffStatus(RentalPaymentSchedule schedule, ThreadLocalClearingResult localResult) {
        BigDecimal totalReceivable = schedule.getTotalReceivableAmount();
        BigDecimal totalReceived = schedule.getPrincipalReceived().add(schedule.getInterestReceived());

        if (totalReceived.compareTo(BigDecimal.ZERO) == 0) {
            schedule.setWriteOffStatus("unwritten");
        } else if (totalReceived.compareTo(totalReceivable) < 0) {
            schedule.setWriteOffStatus("partially_written");
        } else {
            schedule.setWriteOffStatus("fully_written");
            localResult.addTotalRecords(1);
        }
        // 使用新的事务来更新状态，避免长时间持有锁
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            rentalPaymentScheduleService.updateById(schedule);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            log.error("更新租金核销状态时发生异常，计划ID：{}", schedule.getId(), e);
        }
    }

    /**
     * 更新银行收款使用状态
     */
    private void updateUsageStatus(BankCollectionRecord record) {
        if (record.getAmountUsed().compareTo(BigDecimal.ZERO) == 0) {
            record.setUsageStatus("unused");
        } else if (record.getAmountUsed().compareTo(record.getPaymentAmount()) < 0) {
            record.setUsageStatus("partially_used");
        } else {
            record.setUsageStatus("fully_used");
        }

        // 使用新的事务来更新状态，避免长时间持有锁
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            bankCollectionRecordService.updateById(record);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            log.error("更新银行收款使用状态时发生异常，记录ID：{}", record.getId(), e);
        }
    }
}